#ifndef __GAME_TIMER_H__
#define __GAME_TIMER_H__
#include <chrono>

template <class Clock, class Duration>
class GameTimer {
public:
	using clock 		= Clock;
	using duration 		= Duration;
	using time_point 	= typename std::chrono::time_point<Clock>;
	using rep 			= typename Duration::rep;

	GameTimer()
		: mBaseTime(clock::now()),
		  mCurrTime(clock::now()),
		  mPrevTime(clock::now()),
		  mDeltaTime(duration(-1)),
		  mPausedTime(duration(-1)),
		  mIsStopped(false) { }

	GameTimer(const GameTimer&) = delete;
	GameTimer& operator = (const GameTimer&) = delete;

	rep getTotalTime() const {
		if (!mIsStopped) {
			return std::chrono::duration_cast<duration>(mCurrTime - mPausedTime - mBaseTime).count();
		}
		else {
			return std::chrono::duration_cast<duration>(mStopTime - mPausedTime - mBaseTime).count();
		}
	}

	rep getDeltaTime() const {
		return !mIsStopped ? mDeltaTime.count() : -1;
	}

	void stop() {
		if (mIsStopped) {
			return;
		}

		mStopTime = std::chrono::steady_clock::now();
		mIsStopped = true;
	}

	void start() {
		if (mIsStopped) {
			mCurrTime = clock::now();
			mPausedTime += std::chrono::duration_cast<duration>(mCurrTime - mStopTime);
			mIsStopped = true;
		}
	}

	void reset() {
		mCurrTime = mPrevTime = mBaseTime = clock::now();
		mPausedTime = duration(0);
		mDeltaTime = duration(0);
		mIsStopped = false;
	}

	void tick() {
		if (mIsStopped) {
			return;
		}

		auto currTime = clock::now();
		mPrevTime = mCurrTime;
		mCurrTime = currTime;

		mDeltaTime = std::chrono::duration_cast<duration>(mCurrTime - mPrevTime);
	}
private:
	time_point mBaseTime;
	time_point mStopTime;	
	time_point mPrevTime;	
	time_point mCurrTime;	

	duration mPausedTime;	
	duration mDeltaTime;	

	bool mIsStopped;
};

using MicroTimer = GameTimer<std::chrono::steady_clock, std::chrono::microseconds>;
using SecTimer   = GameTimer<std::chrono::steady_clock, std::chrono::seconds>;
using MinTimer   = GameTimer<std::chrono::steady_clock, std::chrono::minutes>;

template <class Timer>
inline float secondCast(const Timer& timer) {
	if constexpr 	  (std::is_same_v<typename Timer::duration, std::chrono::microseconds>) {
		return (float)timer.getDeltaTime() * 1 / 1000000.0f;
	}
	else if constexpr (std::is_same_v<typename Timer::duration, std::chrono::seconds>) {
		return (float)timer.getDeltaTime();
	}
	else if constexpr (std::is_same_v<typename Timer::duration, std::chrono::minutes>) {
		return (float)timer.getDeltaTime() * 60.0f;
	}

	return (float)timer.getDeltaTime();
}

#endif